#pragma once
#include "any.hpp"
#include "channel.hpp"
#include "buffer.hpp"
#include "socket.hpp"


namespace Muduo
{
    typedef enum
    {   
        DISCONNECTED,//链接关闭状态
        CONNECTING,//建立连接成功-待处理状态
        CONNECTED,//链接建立完成,各种设置已经完成,可以通信的状态
        DISCONNECTING//待关闭状态
    }ConnStatu;
    
    class Connection :public std::enable_shared_from_this<Connection>
    {
    public:
        using PtrConnection = std::shared_ptr<Connection>;
        //组件使用者,服务器模块进行设置
        using ConnectedCallback = std::function<void(const PtrConnection&)>;//连接建立成功的回调函数
        using MessageCallback = std::function<void(const PtrConnection&,Buffer *)>;//消息到来的回调函数
        using ClosedCallback = std::function<void(const PtrConnection&)>;//连接关闭的回调函数
        using AnyEventCallback = std::function<void(const PtrConnection&)>;//任意事件触发的回调函数
        Connection(EventLoop * loop,uint64_t conn_id,int sockfd):_conn_id(conn_id),_sockfd(sockfd),_enable_inactive_release(false),
        _loop(loop),_statu(CONNECTING),_socket(_sockfd),_channel(loop,_sockfd)
        {
            _channel.SetCloseCallback(std::bind(&Connection::HandlerClose,this));
            _channel.SetEventCallback(std::bind(&Connection::HandlerEvent,this));
            _channel.SetReadCallback(std::bind(&Connection::HandlerRead,this));
            _channel.SetWriteCallback(std::bind(&Connection::HandlerWrite,this));
            _channel.SetErrorCallback(std::bind(&Connection::HandlerError,this));
        }

        ~Connection()
        {
            DLOG("release connection:%p",this);
        }

        //获取管理的文件描述符
        int Fd() 
        {
            return _sockfd;
        }

        //获取连接id
        int Id()
        {
            return _conn_id;
        }

        //检测是否属于CONNECTED状态
        bool Connected()
        {
            return _statu == CONNECTED;
        }

        //设置上下文--连接建立完成时进行调用
        void SetContext(const Any&context)
        {
            _context = context;
        }

        //获取上下文
        Any * GetContext()
        {
            return &_context;
        }

        void SetConnectedCallback(const ConnectedCallback& cb)
        {
            _connected_callback = cb;
        }

        void SetMessageCallback(const MessageCallback & cb)
        {
            _message_callback = cb;
        }

        void SetClosedCallback(const ClosedCallback & cb)
        {
            _closed_callback = cb;
        }

        void SetAnyEventCallback(const AnyEventCallback & cb)
        {
            _event_callback = cb;
        }

        void SetSrvClosedCallback(const ClosedCallback & cb)
        {
            _server_closed_callback= cb;
        }

        //连接建立就绪后,进行channel回调设置,启动读监控,调用_connected_callback
        void Established()
        {
            _loop->RunInLoop(std::bind(&Connection::EstablishedInLoop,this));
        }

        //发送数据,将数据放到发送缓冲区,启动写事件监控
        void Send(const char * data,size_t len)
        {
            //外界传入的data,可能是个临时空间,我们现在只是把发送操作压入了任务池,有可能
            //没有被立即执行,因此有可能执行的时候,data指向的空间已经被释放了
            Buffer buf;
            buf.WriteAndPush(data,len);
            _loop->RunInLoop(std::bind(&Connection::SendInLoop,this,std::move(buf)));
        }

        //提供给组件使用者的关闭接口->并不实际关闭,需要判断有没有数据待处理
        void Shutdown()
        {
            _loop->RunInLoop(std::bind(&Connection::ShutdownInLoop,this));
        }
        
        void Release()
        {
            _loop->QueueInLoop(std::bind(&Connection::ReleaseInLoop,this));
        }

        //启动非活跃销毁,并定义多长时间无通信就是非活跃
        void EnableInactiveRelease(int sec)
        {
            _loop->RunInLoop(std::bind(&Connection::EnableInactiveReleaseInLoop,this,sec));
        }

        //取消非活跃销毁
        void CancelInactiveRelease()
        {
            _loop->RunInLoop(std::bind(&Connection::CancelInactiveReleaseInLoop,this));
        }

        //切换协议->重置上下文以及阶段性回调函数,而且这个接口必须在EventLoop线程中立即执行
        void Upgrade(const Any&context,const ConnectedCallback&conn,const MessageCallback & msg,const ClosedCallback & closed,const AnyEventCallback&event)
        {
            _loop->AssertInLoop();
            _loop->RunInLoop(std::bind(&Connection::UpgradeInLoop,this,context,conn,msg,closed,event));
        }

    private:
        //五个channel的事件回调函数
        //描述符触发可读事件之后,接收socket数据放到缓冲区中,然后调用_message_callback
        void HandlerRead()
        {
            //接收socket数据
            char buf[65536] = {0};
            ssize_t ret = _socket.NonBlockRecv(buf,65535);
            if(ret<0)
            {
                //出错了,不能直接关闭连接
                return ShutdownInLoop();
            }
            //等于0表示没有读取到数据,不是连接断开了,连接断开返回-1
            _in_buffer.WriteAndPush(buf,ret);
            //调用_message_callback进行业务处理
            if(_in_buffer.ReadAbleSize()>0)
            {
                //shared_from_this-从当前对象自身获取自身的shared_ptr管理对象
                //Constructs and returns a shared_ptr object pointing to *this and sharing ownership with existing shared_ptr objects.
                return _message_callback(shared_from_this(),&_in_buffer);
            }
        }

        //描述符可写事件触发后调用的函数,将缓冲区中的数据进行发送
        void HandlerWrite()
        {
            //_out_buffer中保存的数据就是要发送的数据
            ssize_t ret = _socket.NonBlockSend(_out_buffer.ReadPosition(),_out_buffer.ReadAbleSize());
            if(ret<0)
            {
                //发送错误,关闭连接
                if(_in_buffer.ReadAbleSize()>0)
                {
                    _message_callback(shared_from_this(),&_in_buffer);
                }
                return Release();//实际的关闭释放操作
            }
            _out_buffer.MoveReadOffset(ret);//将读偏移向后移动
            if(_out_buffer.ReadAbleSize()==0)
            {
                _channel.DisableWrite();//没有数据待发送了,关闭写事件监控
                //如果当前是连接待关闭状态,则有数据,发送完数据释放连接,没有数据则直接释放
                if(_statu == DISCONNECTING)
                {
                    return Release();
                }
            }
        }

        //描述符触发挂断事件
        void HandlerClose()
        {
            //一旦连接断开,套接字就什么都不做了,因此有数据待处理则处理,关闭连接
            if(_in_buffer.ReadAbleSize()>0)
            {
                _message_callback(shared_from_this(),&_in_buffer);
            }
            Release();
        }

        //描述符触发出错事件
        void HandlerError()
        {
            HandlerClose();
        }

        //描述符触发任意事件:1,刷新连接的活跃度->延迟定时销毁任务  2,调用组件使用者的任意事件回调
        void HandlerEvent()
        {
            if(_enable_inactive_release == true)
            {
                _loop->TimerRefresh(_conn_id);
            }
            if(_event_callback)
            {
                _event_callback(shared_from_this());
            }
        }

        //连接获取之后,所处的状态下要进行各种设置(启动读监控,调用回调函数)
        void EstablishedInLoop()
        {
            //修改连接状态,启动读事件监控,调用回调函数
            assert(_statu==CONNECTING);//当前的状态一定是半链接状态
            _statu = CONNECTED;//当前函数执行完毕,进入已完成链接状态
            //一旦启动读事件监控就有可能会立即触发读事件,
            _channel.EnableRead();
            if(_connected_callback) _connected_callback(shared_from_this());
        }

        //实际的释放接口
        void ReleaseInLoop()
        {
            //修改连接状态
            _statu = DISCONNECTED;
            //移除连接的事件监控
            _channel.Remove();
            //关闭描述符
            _socket.Close();
            //如果当前定时器队列中还有定时销毁任务,则取消任务
            if(_loop->HasTimer(_conn_id)) CancelInactiveReleaseInLoop();
            //调用关闭回调函数,避免先移除服务器管理的链接信息导致Connection被释放,再去处理会出错
            if(_closed_callback) _closed_callback(shared_from_this());
            //移除服务器内部管理的链接信息
            if(_server_closed_callback) _server_closed_callback(shared_from_this());
        }

        //把数据放到发送缓冲区中,启动可写事件监控
        void SendInLoop(Buffer buf)
        {
            if(_statu == DISCONNECTED) return;
            _out_buffer.WriteBufferAndPush(buf);
            if(_channel.WriteAble()== false)
            {
                _channel.EnableWrite();
            }
        }

        //关闭连接,需要判断还有没有数据待处理,待发送
        void ShutdownInLoop()
        {
            _statu = DISCONNECTING;//设置连接为半关闭状态
            if(_in_buffer.ReadAbleSize()>0)
            {
                if(_message_callback) _message_callback(shared_from_this(),&_in_buffer);
            }
            //写入数据的时候出错关闭,没有待发送数据,直接关闭
            if(_out_buffer.ReadAbleSize()>0)
            {
                if(_channel.WriteAble() == false)
                {
                    _channel.EnableWrite();
                }
            }
            if(_out_buffer.ReadAbleSize() == 0)
            {
                Release();
            }
        }

        //启动非活跃连接超时释放规则
        void EnableInactiveReleaseInLoop(int sec)
        {
            //将判断标志_enable_inactive_release置为true
            _enable_inactive_release = true;
            //如果当前定时销毁任务存在,刷新
            if(_loop->HasTimer(_conn_id))
            {
                return _loop->TimerRefresh(_conn_id);
            }
            //不存在定时销毁任务,新增
            _loop->TimerAdd(_conn_id,sec,std::bind(&Connection::Release,this));
        }

        //取消非活跃连接超时释放规则
        void CancelInactiveReleaseInLoop()
        {
            _enable_inactive_release = false;
            if(_loop->HasTimer(_conn_id))
            {
                _loop->TimerCancel(_conn_id);
            }
        }

        //更新上下文处理协议
        void UpgradeInLoop(const Any&context,const ConnectedCallback & conn,const MessageCallback & msg
        ,const ClosedCallback & closed,const AnyEventCallback& event)
        {
            _context = context;
            _connected_callback = conn;
            _message_callback = msg;
            _closed_callback = closed;
            _event_callback = event;
        }

    private:
        uint64_t _conn_id;//链接的唯一ID,便于链接的管理和查找,同时定时器ID共用一个id
        int _sockfd;//链接关联的文件描述符
        bool _enable_inactive_release;//链接是否启动非活跃销毁的判断标志,默认false
        EventLoop * _loop;//链接所关联的一个eventloop
        ConnStatu _statu;//链接状态
        Socket _socket;//套接字操作管理
        Channel _channel;//链接的事件管理
        Buffer _in_buffer;//输入缓冲区--存放从socket中读取到的数据
        Buffer _out_buffer;//输出缓冲区--存放要发送给对方的数据
        Any _context;//请求的接收处理上下文

        //四个回调函数,让服务器模块来设置
        //四个回调都是组件使用者使用的
        ConnectedCallback _connected_callback;
        MessageCallback _message_callback;
        ClosedCallback _closed_callback;
        AnyEventCallback _event_callback;
        
        //组件内的链接关闭回调->组件内设置,因为服务器组件内会把所有的链接管理起来,一旦某个链接要关闭,就应该从管理的地方移除掉自己的信息
        ClosedCallback _server_closed_callback;
    };
}